<template>
  <div>
    <div
      class="container"
      @mousedown="containerMousedown"
      @mousemove="containerMousemove"
      @mouseup="containerMouseup"
      ref="container"
    >
      <div
        ref="row"
        v-for="(item, index) in gridData" :key="index"
        class="row"
      >
        <template v-for="(ch_item, ch_index) in item" >
          <template v-if="ch_item">
            <tite
              @moved="titeMoveEnd"
              :key="ch_index"
              :tite="ch_item"
            ></tite>
            <tite
              @moved="titeMoveEnd"
              :key="ch_index"
              v-if="ch_item.mergedFrom"
              :tite="ch_item.mergedFrom[1]"
            ></tite>
          </template>
        </template>
      </div>
    </div>
  </div>
</template>

<script lang="ts">
import '../interface'
import tite from '@/views/2048/2048_2/tite.vue'
import {
  ref,
  defineComponent,
  onMounted
} from 'vue'

const directionMap = {
  left: 0,
  right: 1,
  up: 2,
  down: 3
}

export default defineComponent({
  name: '2048',
  components: {
    tite
  },
  setup () {
    const gridData = ref<Array<Array<Ceil | null>>>([
      [null, null, null, null],
      [null, null, null, null],
      [null, null, null, null],
      [null, null, null, null]
    ])
    const size = 4
    let moved = false
    let addFlag = true
    let clientX: number | null = null
    let clientY: number | null = null

    const getVector = (direction: number) => {
      const map: VectorMap = {
        [directionMap.up]: { x: 0, y: -1 },
        [directionMap.right]: { x: 1, y: 0 },
        [directionMap.down]: { x: 0, y: 1 },
        [directionMap.left]: { x: -1, y: 0 }
      }

      return map[direction]
    }

    const buildTraversals = (vector: CeilPosition) => {
      const traversals: TraversalsMap = { x: [], y: [] }

      for (let pos = 0; pos < size; pos++) {
        traversals.x.push(pos)
        traversals.y.push(pos)
      }

      if (vector.x === 1) traversals.y = traversals.y.reverse()
      if (vector.y === 1) traversals.x = traversals.x.reverse()
      return traversals
    }

    const checkRoom = () => {
      const arr = []
      for (let n = 0; n < size; n++) {
        for (let i = 0; i < size; i++) {
          if (!gridData.value[n][i]) {
            arr.push({ x: n, y: i })
          }
        }
      }
      return arr
    }

    function removeCeil (position: CeilPosition) {
      gridData.value[position.x][position.y] = null
    }

    function updateCeil (ceil: Ceil, position: CeilPosition) {
      ceil.previousPosition = {
        x: ceil.x,
        y: ceil.y
      }
      ceil.x = position.x
      ceil.y = position.y
      return ceil
    }

    function updateGrid (ceil: Ceil, position: CeilPosition) {
      if (ceil.x === position.x && ceil.y === position.y) {
        return ceil
      }
      removeCeil({ x: ceil.x, y: ceil.y })
      ceil = updateCeil(ceil, position)
      gridData.value[position.x][position.y] = ceil
      return gridData.value[position.x][position.y]
    }

    function checkBoundary (position: CeilPosition) {
      return position.x >= 0 && position.x < size &&
        position.y >= 0 && position.y < size
    }

    function checkExist (position: CeilPosition) {
      if (checkBoundary(position)) {
        return gridData.value[position.x][position.y]
      } else {
        return null
      }
    }

    function matchesAvailable () {
      for (let n = 0; n < size; n++) {
        for (let i = 0; i < size; i++) {
          const ceil = checkExist({ x: n, y: i })
          if (ceil) {
            for (let d = 0; d < 4; d++) {
              const v = getVector(d)
              const next = checkExist({
                x: n + v.x,
                y: i + v.y
              })
              if (next && (next.value === ceil.value)) {
                return true
              }
            }
          }
        }
      }
      return false
    }

    function gameOver () {
      return !checkRoom().length && !matchesAvailable()
    }

    function checkAvailable (position: CeilPosition) {
      return !checkExist(position)
    }

    function moveCeil (position: CeilPosition, vector: CeilPosition) {
      let previous
      do {
        previous = position
        position = {
          x: previous.x + vector.y,
          y: previous.y + vector.x
        }
      } while (checkBoundary(position) && checkAvailable(position))

      return {
        previous,
        next: position
      }
    }

    function generateCeil (position: CeilPosition, value?: number) {
      return {
        value: value || 2,
        x: position.x as number,
        y: position.y as number,
        previousPosition: null,
        mergedFrom: null,
        del: false
      }
    }

    function randomGenerateCeil (arr: Array<CeilPosition>) {
      const position: CeilPosition = arr[Math.floor(arr.length * Math.random())]
      gridData.value[position.x][position.y] = (generateCeil(position) as Ceil)
    }

    function addCeil () {
      randomGenerateCeil(checkRoom())
    }

    const checkMove = (direction: number) => {
      const vector = getVector(direction)
      const traversals = buildTraversals(vector)
      for (let n = 0; n < traversals.x.length; n++) {
        for (let i = 0; i < traversals.y.length; i++) {
          const ceilPosition: CeilPosition = {
            x: traversals.x[n],
            y: traversals.y[i]
          }
          let ceil = checkExist(ceilPosition)
          if (ceil) {
            const positions = moveCeil(ceilPosition, vector)
            const next = checkExist(positions.next)
            if (next && next.value === ceil.value && !next.mergedFrom) {
              const newCeil = generateCeil(positions.next as CeilPosition, ceil.value * 2)
              ceil = updateCeil(ceil, positions.next)
              next.mergedFrom = [newCeil, ceil as Ceil]
              removeCeil(ceilPosition)
            } else {
              ceil = updateGrid(ceil, positions.previous)
            }
          }
          if (ceil && (ceil.x !== ceilPosition.x || ceil.y !== ceilPosition.y)) {
            moved = true
          }
        }
      }

      // if (moved) {
      // }
    }

    const containerMousedown = (e: MouseEvent) => {
      e.preventDefault()
      addFlag = true
      clientX = e.clientX
      clientY = e.clientY
    }

    const containerMousemove = (e: MouseEvent) => {
      e.preventDefault()
    }

    const containerMouseup = (e: MouseEvent) => {
      e.preventDefault()
      const x = e.clientX - (clientX as number)
      const absX = Math.abs(x)
      const y = e.clientY - (clientY as number)
      const absY = Math.abs(y)
      let direction: number | null = null
      if (Math.max(absX, absY) > 10) {
        direction = absX > absY
          ? (x > 0 ? directionMap.right : directionMap.left)
          : (y > 0 ? directionMap.down : directionMap.up)
        console.info(direction)
        checkMove(direction)
      }
    }

    function titeMoveEnd () {
      if (moved && addFlag) {
        gridData.value = gridData.value.map(item => {
          return item.map(chItem => {
            if (chItem) {
              chItem.previousPosition = null
              if (chItem.mergedFrom) {
                chItem = chItem.mergedFrom[0]
              }
            }
            return chItem
          })
        })
        addFlag = false
        moved = false
        setTimeout(() => {
          addCeil()
          if (gameOver()) {
            console.info('游戏结束')
          }
        }, 100)
      }
    }

    onMounted(() => {
      randomGenerateCeil(checkRoom() as Array<CeilPosition>)
      // updateView()
    })
    return {
      gridData,
      titeMoveEnd,
      containerMousedown,
      containerMousemove,
      containerMouseup
    }
  }
})
</script>

<style scoped lang="less">
  .container {
    width: 200px;
    height: 200px;
    border: 1px solid #e0e0e0;
    border-radius: 3px;
    display: flex;
    flex-wrap: wrap;
    user-select: none;
    position: relative;
    .row {
      .col {
        transition: all .3s;
        position: absolute;
        box-sizing: border-box;
        width: 50px;
        height: 50px;
        border: 3px solid #42b983;
        display: flex;
        align-items: center;
        justify-content: center;
      }
    }
  }
</style>
